﻿//----------------------------------------------------------------
// <copyright file="Parser.cs" >
//    Top Do ---TOP API DEMO 示例系列之
//    CLR Version: 2.0.50727.1434
//    NameSpace:  TopSpace.Bll
//    FileClass: Parser - XML对象解析器
//
//    By 淘宝旺旺ID: 幻影时空
//    Email:visionspace@st-soft.cn
//    Copyright (C) SpaceTime SoftWare , All rights reserved (www.st-soft.cn).
//
// </copyright>
//----------------------------------------------------------------
using System;
using System.Net;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Security.Cryptography;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;

namespace TopSpace.Bll
{
    public class Parser
    {
        /// <summary>
        /// 获取记录总数
        /// </summary>
        /// <param name="strXml">XML内容字符串</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <returns></returns>
        public int XmlToTotalResults(string strXml,string rootName)
        {
            int total=0;
            try
            {
                if (strXml.Trim() == "")//如果为空，则退出
                {
                    return 0;
                }
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(strXml);
                if (xml.SelectNodes(rootName + "_response/total_results").Count != 0)
                {
                    total = Convert.ToInt32(xml.SelectSingleNode(rootName + "_response/total_results").InnerText);
                }
            }
            catch(Exception exp)
            {
                //
            }
            return total;
        }

        /// <summary>
        /// 根据XML返回指定节点单个文本内容
        /// </summary>
        /// <param name="strXml">xml内容</param>
        /// <param name="nodePath">完整的节点路径（包括根路径）</param>
        /// <returns></returns>
        public string XmlToNodeText(string strXml, string nodePath)
        {
            if (strXml.Trim() == "")//如果为空，则退出
            {
                return "";
            }
            try
            {
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(strXml);
                if (xml.SelectSingleNode(nodePath) != null)
                {
                    return xml.SelectSingleNode(nodePath).InnerText;
                }
                else
                {
                    return "";
                }
            }
            catch (Exception exp)
            {
                return "";
            }
         }

        #region ------- API v2.0 对象解析器 ------

        /// <summary>
        /// 根据XML返回数据对象 -- 标准方法
        /// </summary>
        /// <param name="strXml">XML内容字符串</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="childName">子节字符串名称(参考返回XML内容，不含_response节的到最后一子节点之间 (如 trades_sold_get_response/trades/trade 则输入 trades/trade ，其中 trade 为集合)，多层使用"/"间隔)</param>
        /// <param name="obj">有效对象</param>
        /// <param name="rsp">错误对象</param>
        /// <returns></returns>
        public bool XmlToObject2(string strXml, string rootName, string childName, object obj, ErrorRsp rsp)
        {
            //throw new Exception(strXml);

            try
            {
                if (strXml.Trim() == "")//如果为空，则退出
                {
                    return false;
                }

                string xmlpath = (childName.Trim() != "" ? rootName + "_response/" + childName : rootName + "_response");

                XmlDocument xml = new XmlDocument();
                xml.LoadXml(strXml);
                if (xml.SelectNodes(xmlpath).Count != 0)
                {
                    XmlNodeList xmlList = xml.SelectNodes(xmlpath);
                    foreach (XmlNode xns in xmlList)
                    {
                        foreach (XmlNode xn in xns)
                        {

                            if (xn.ChildNodes.Count == 1)
                            {
                                XmlToObject(obj, xn.Name, xn.InnerText);
                            }
                            else
                            {
                                for (int i = 0; i < xn.ChildNodes.Count; i++)
                                {
                                    XmlToObject(xn.Name, obj, xn.ChildNodes[i].Name, xn.ChildNodes[i].InnerText);
                                }
                            }
                        }
                    }
                    return true;
                }
                else if (IsXmlError2(xml, rsp))
                {
                    return false;
                }
                else
                {
                    rsp.code = "-1";
                    return false;
                }
            }
            catch (Exception exp)
            {
                rsp.args = strXml;
                rsp.IsError = true;
                rsp.code = "8008";
                rsp.msg = "连接服务器发生错误！" + exp.Message;
                return false;
            }
        }

        /// <summary>
        /// 根据XML返回数据对象 - 根路径方法
        /// </summary>
        /// <param name="strXml">XML内容字符串</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="obj">有效对象</param>
        /// <param name="rsp">错误对象</param>
        /// <returns></returns>
        public bool XmlToObject2(string strXml, string rootName, object obj, ErrorRsp rsp)
        {
            return XmlToObject2(strXml, rootName, "", obj, rsp);
        }

        /// <summary>
        /// 根据XML返回数据泛型对象 -- 泛型方法
        /// </summary>
        /// <typeparam name="T">子泛型类型</typeparam>
        /// <param name="strXml">XML</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="childName">子节字符串名称(参考返回XML内容，不含_response节的到最后一子节点之间 (如 trades_sold_get_response/trades/trade 则输入 trades/trade ，其中 trade 为集合)，多层使用"/"间隔)</param>
        /// <param name="objs">有效泛型对象</param>
        /// <returns></returns>
        public bool XmlToObject2<T>(string strXml, string rootName, string childName, List<T> objs)
        {
            return XmlToObject2<T>(strXml, rootName, childName, objs, new ErrorRsp());
        }

        /// <summary>
        /// 根据XML返回数据泛型对象 -- 泛型方法
        /// </summary>
        /// <typeparam name="T">子泛型类型</typeparam>
        /// <param name="strXml">XML</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="childName">子节字符串名称(参考返回XML内容，不含_response节的到最后一子节点之间 (如 trades_sold_get_response/trades/trade 则输入 trades/trade ，其中 trade 为集合)，多层使用"/"间隔)</param>
        /// <param name="objs">有效泛型对象</param>
        /// <param name="rsp">错误对象</param>
        /// <returns></returns>
        public bool XmlToObject2<T>(string strXml, string rootName, string childName, List<T> objs, ErrorRsp rsp)
        {
            try
            {
                if (strXml.Trim() == "")//如果为空，则退出
                {
                    return false;
                }

                XmlDocument xml = new XmlDocument();
                xml.LoadXml(strXml);

                if (xml.SelectNodes(rootName + "_response").Count != 0)
                {
                    XmlNodeList xmlList = xml.SelectNodes((childName.Trim() != "" ? rootName + "_response/" + childName : rootName + "_response"));
                    foreach (XmlNode xns in xmlList)
                    {
                        T obj = (T)Activator.CreateInstance(typeof(T));

                        foreach (XmlNode xn in xns)
                        {
                            if (xn.ChildNodes.Count == 1)
                            {
                                XmlToObject(obj, xn.Name, xn.InnerText);
                            }
                            else
                            {
                                for (int i = 0; i < xn.ChildNodes.Count; i++)
                                {
                                    XmlToObject(xn.Name, obj, xn.ChildNodes[i].Name, xn.ChildNodes[i].InnerText);
                                }
                            }
                        }
                        objs.Add(obj);
                    }

                    return true;
                }
                else if (IsXmlError2(xml, rsp))
                {
                    return false;
                }
                else
                {
                    rsp.code = "-1";

                    return false;
                }
            }
            catch (Exception exp)
            {
                rsp.args = strXml;
                rsp.IsError = true;
                rsp.code = "8008";
                rsp.msg = "连接服务器发生错误！" + exp.Message;
                return false;
            }
        }

        /// <summary>
        /// 根据XML返回数据泛型对象 -- 泛型方法（子、孙节）
        /// </summary>
        /// <typeparam name="T">孙节泛型类型</typeparam>
        /// <param name="strXml">XML</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="childName">子节字符串名称(参考返回XML内容，不含_response节的到最后一子节点之间 (如 trades_sold_get_response/trades/trade 则输入 trades/trade ，其中 trade 为集合)，多层使用"/"间隔)</param>
        /// <param name="childKeyValue">子节值关键词（判断是否位于当前子节，从List(T)对象中取任意成员主键代表）</param>
        /// <param name="grandChildName">孙子节字符串名称（根据子节延续，表达孙子节名称，不含_response/子节，直接为孙节代表）如 如 trades_sold_get_response/trades/trade/orders/order 则输入 orders/order ，其中 order 为集合</param>
        /// <param name="objs">有效泛型对象</param>
        /// <returns></returns>
        public bool XmlToObject2<T>(string strXml, string rootName, string childName, string childKeyValue, string grandChildName, List<T> objs)
        {
            return XmlToObject2<T>(strXml, rootName, childName, childKeyValue, grandChildName, objs, new ErrorRsp());
        }

        /// <summary>
        /// 根据XML返回数据泛型对象 -- 泛型方法（子、孙节）
        /// </summary>
        /// <typeparam name="T">孙节泛型类型</typeparam>
        /// <param name="strXml">XML</param>
        /// <param name="rootName">根节点名称 [***_***_response]([***_get_response]|[***_add_response]|[***_upload_response]) 即 API方法名 去除 taobao 后将.换成_显示出来的根节点，如 taobao.user.get 则换成 user_get , taobao.item.add 则换成 item_add</param>
        /// <param name="childName">子节字符串名称(参考返回XML内容，不含_response节的到最后一子节点之间 (如 trades_sold_get_response/trades/trade 则输入 trades/trade ，其中 trade 为集合)，多层使用"/"间隔)</param>
        /// <param name="childKeyValue">子节值关键词（判断是否位于当前子节，从List(T)对象中取任意成员主键代表）</param>
        /// <param name="grandChildName">孙子节字符串名称（根据子节延续，表达孙子节名称，不含_response/子节，直接为孙节代表）如 如 trades_sold_get_response/trades/trade/orders/order 则输入 orders/order ，其中 order 为集合</param>
        /// <param name="objs">有效泛型对象</param>
        /// <param name="rsp">错误对象</param>
        /// <returns></returns>
        public bool XmlToObject2<T>(string strXml, string rootName, string childName, string childKeyValue, string grandChildName, List<T> objs, ErrorRsp rsp)
        {
            try
            {
                if (strXml.Trim() == "")//如果为空，则退出
                {
                    return false;
                }

                XmlDocument xml = new XmlDocument();
                xml.LoadXml(strXml);

                if (xml.SelectNodes(rootName + "_response").Count != 0)
                {
                    XmlNodeList xmlList = xml.SelectNodes((childName.Trim() != "" ? rootName + "_response/" + childName : rootName + "_response"));
                    foreach (XmlNode xns in xmlList)
                    {
                        if (xns.InnerText.Contains(childKeyValue))
                        {
                            XmlNodeList grandChild = xns.SelectNodes(grandChildName);
                            if (grandChild.Count > 0)
                            {
                                foreach (XmlNode gxns in grandChild)
                                {
                                    if (gxns.ParentNode.ParentNode.InnerText.Contains(childKeyValue))
                                    {

                                        T obj = (T)Activator.CreateInstance(typeof(T));
                                        foreach (XmlNode xn in gxns)
                                        {
                                            if (xn.ChildNodes.Count == 1)
                                            {
                                                XmlToObject(obj, xn.Name, xn.InnerText);
                                            }
                                            else
                                            {
                                                for (int i = 0; i < xn.ChildNodes.Count; i++)
                                                {
                                                    XmlToObject(xn.Name, obj, xn.ChildNodes[i].Name, xn.ChildNodes[i].InnerText);
                                                }
                                            }
                                        }
                                        objs.Add(obj);
                                    }
                                }
                            }
                        }
                    }

                    return true;
                }
                else if (IsXmlError2(xml, rsp))
                {
                    return false;
                }
                else
                {
                    rsp.code = "-1";

                    return false;
                }
            }
            catch (Exception exp)
            {
                rsp.args = strXml;
                rsp.IsError = true;
                rsp.code = "8008";
                rsp.msg = "连接服务器发生错误！" + exp.Message;
                return false;
            }
        }

        /// <summary>
        /// 判断 是否XMl错误代码 v2.0 
        /// </summary>
        /// <param name="xml"></param>
        /// <param name="rsp"></param>
        /// <returns></returns>
        public bool IsXmlError2(XmlDocument xml, ErrorRsp rsp)
        {
            if (xml.SelectNodes("error_response").Count != 0)
            {
                XmlErrorToObject2(xml, rsp);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 错误参数转换 API 2.0
        /// </summary>
        /// <param name="xml"></param>
        /// <param name="rsp"></param>
        public void XmlErrorToObject2(XmlDocument xml, ErrorRsp rsp)
        {
            StringBuilder errArgs = new StringBuilder();
            XmlNodeList argsList = xml.SelectNodes("error_response/args/arg");
            foreach (XmlNode args in argsList)
            {
                if (args.Name == "arg")
                {
                    errArgs.Append(args.SelectNodes("key").Item(0).InnerText + "=" + args.SelectNodes("value").Item(0).InnerText + "|");
                }
            }
            rsp.args = errArgs.ToString();
            rsp.IsError = true;

            rsp.code = xml.SelectSingleNode("error_response/code").InnerText;
            rsp.msg = xml.SelectSingleNode("error_response/msg").InnerText;
            rsp.sub_code = xml.SelectSingleNode("error_response/sub_code") != null ? xml.SelectSingleNode("error_response/sub_code").InnerText : "";
            rsp.sub_msg = xml.SelectSingleNode("error_response/sub_msg") != null ? xml.SelectSingleNode("error_response/sub_msg").InnerText : "";

        }

        #endregion// API 2.0

        #region ---- API2.0 对象解析器之 值转换 -----

        /// <summary>
        /// 根据XML值设置对应属性值
        /// </summary>
        /// <param name="put"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        protected void XmlToObject(object oPut, string name, string value)
        {
            System.ComponentModel.PropertyDescriptor pd = System.ComponentModel.TypeDescriptor.GetProperties(oPut).Find(name, true);
            if (pd != null)
            {
                if (pd.PropertyType == typeof(int) || pd.PropertyType == typeof(long) || pd.PropertyType == typeof(string) || pd.PropertyType == typeof(DateTime) || pd.PropertyType == typeof(bool))
                {
                    if (pd.PropertyType == typeof(int))
                    {
                        pd.SetValue(oPut, System.Convert.ToInt32(value));
                    }
                    else if (pd.PropertyType == typeof(long))
                    {
                        pd.SetValue(oPut, System.Convert.ToInt64(value));
                    }
                    else if (pd.PropertyType == typeof(double))
                    {
                        pd.SetValue(oPut, System.Convert.ToDouble(value));
                    }
                    else if (pd.PropertyType == typeof(bool))
                    {
                        pd.SetValue(oPut, System.Convert.ToBoolean(value));
                    }
                    else
                    {
                        pd.SetValue(oPut, value);
                    }
                }
            }
            else
            {
                //对象为空
            }
        }

        /// <summary>
        /// 根据XML值设置对应的属性值
        /// </summary>
        /// <param name="ParentName">父级对象名称</param>
        /// <param name="put"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        protected void XmlToObject(string parentName, object oPut, string name, string value)
        {
            System.ComponentModel.PropertyDescriptor pd = System.ComponentModel.TypeDescriptor.GetProperties(oPut).Find(parentName, true);
            if (pd != null)
            {
                System.ComponentModel.PropertyDescriptor childPut = System.ComponentModel.TypeDescriptor.GetProperties(pd.GetValue(oPut)).Find(name, true);
                if (childPut != null)
                {
                    if (childPut.PropertyType == typeof(int) || childPut.PropertyType == typeof(long) || childPut.PropertyType == typeof(string) || childPut.PropertyType == typeof(DateTime) || childPut.PropertyType == typeof(bool))
                    {
                        if (childPut.PropertyType == typeof(int))
                        {
                            childPut.SetValue((pd.GetValue(oPut)), System.Convert.ToInt32(value));
                        }
                        else if (childPut.PropertyType == typeof(long))
                        {
                            childPut.SetValue((pd.GetValue(oPut)), System.Convert.ToInt64(value));
                        }
                        else if (childPut.PropertyType == typeof(bool))
                        {
                            childPut.SetValue((pd.GetValue(oPut)), System.Convert.ToBoolean(value));
                        }
                        else
                        {
                            childPut.SetValue((pd.GetValue(oPut)), value);
                        }

                    }
                }
                else
                {
                    //为空
                }
            }
            else
            {
                //为空
            }
        }

        #endregion


        /// <summary>
        /// 解析回调地址中top_parameters中的值
        /// </summary>
        /// <param name="key">取值关键词</param>
        /// <returns></returns>
        public string GetParameters(string parameters,string key)
        {
            string ret = string.Empty;
            try
            {                
                string str = Base64ToString(parameters);
                string[] param = str.Split('&');
                for (int i = 0; i < param.Length; i++)
                {
                    string[] info = param[i].Split('=');
                    if (info[0].ToLower() == key.ToLower())
                    {
                        ret = info[1];
                        break;
                    }
                }
            }
            catch (Exception exp)
            { 
                //
            }
            return ret;
        }

        /// <summary>
        /// 转换对象到参数
        /// </summary>
        /// <param name="obj">对象</param>
        /// <returns></returns>
        public SqlParameter[] ConvertToParams(object obj)
        {
            return ConvertToParams(obj, null);
        }

        /// <summary>
        /// 转换对象到参数
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="commandParameters">补充参数</param>
        /// <returns></returns>
        public SqlParameter[] ConvertToParams(object obj, params SqlParameter[] commandParameters)
        {
            IDictionary<string, string> parmList = new SortedDictionary<string, string>();

            #region -----返回类对象参数-------
            System.Reflection.PropertyInfo[] objs = obj.GetType().GetProperties();
            foreach (System.Reflection.PropertyInfo pi in objs)
            {
                string name = pi.Name;
                if (pi.PropertyType == typeof(int) || pi.PropertyType == typeof(long) || pi.PropertyType == typeof(string) || pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(bool) || pi.PropertyType == typeof(double))
                {
                    string value = Convert.ToString(pi.GetValue(obj, null));

                    if (pi.PropertyType == typeof(int))//是否为数字类型
                    {
                        parmList.Add(name, value);
                    }
                    else if (pi.PropertyType == typeof(long))//是否为数字长类型
                    {
                        parmList.Add(name, value);
                    }
                    else
                    {
                        parmList.Add(name, value);
                    }
                }
                else if (pi.PropertyType.IsGenericType == true)//是否为泛型类 -- 忽略
                {
                    //泛型类 -- 忽略
                }
                else //其它自定义类对象 -- 忽略
                {
                    #region -----自定义子对象------

                    System.Reflection.PropertyInfo[] childPs = (pi.GetValue(obj, null)).GetType().GetProperties();//o.GetType().GetProperties();
                    foreach (System.Reflection.PropertyInfo childPi in childPs)
                    {
                        string childName = childPi.Name;
                        if (childPi.PropertyType == typeof(int) || childPi.PropertyType == typeof(long) || childPi.PropertyType == typeof(string) || childPi.PropertyType == typeof(DateTime) || childPi.PropertyType == typeof(bool) || childPi.PropertyType == typeof(double))
                        {
                            string value = Convert.ToString(childPi.GetValue(pi.GetValue(obj, null), null));

                            parmList.Add(name + childName, value);
                        }
                    }

                    #endregion

                }
            }
            #endregion

            SqlParameter[] param = new SqlParameter[parmList.Count + (commandParameters != null ? commandParameters.Length : 0)];

            int i = 0;
            foreach (KeyValuePair<string, string> kvp in parmList)
            {
                if (kvp.Key.Contains("price") || kvp.Key.Contains("_fee") || kvp.Key.Contains("payment")) //money 类型
                {
                    param[i] = new SqlParameter("@" + kvp.Key.Replace("_", ""), SqlDbType.Money);
                    param[i].Value = kvp.Value;
                }
                else if (kvp.Key.Contains("time") || kvp.Key.Contains("modified"))//datetime 类型
                {
                    param[i] = new SqlParameter("@" + kvp.Key.Replace("_", ""), SqlDbType.DateTime);
                    param[i].Value = kvp.Value;
                }
                else if (kvp.Key.Contains("desc"))//ntext 类型
                {
                    param[i] = new SqlParameter("@" + kvp.Key.Replace("_", ""), SqlDbType.NText);
                    param[i].Value = kvp.Value;
                }
                else
                {
                    param[i] = new SqlParameter("@" + kvp.Key.Replace("_", ""), kvp.Value);
                }
                i++;
            }

            if (commandParameters != null)
            {
                foreach (SqlParameter par in commandParameters)
                {
                    param[i] = par;
                    i++;
                }
            }
            return param;
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public string StringToBase64(string str)
        {
            return System.Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(str));
        }

        /// <summary>
        /// Base64 解密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public string Base64ToString(string str)
        {
            return System.Text.Encoding.Default.GetString(System.Convert.FromBase64String(str));
        }
    }
}
